#include "Naview.h"
#include "StringOps.h"
#include "Neptuner.h"

#include<iostream>

using namespace std;

#include "Keywords.h"
#include "Scope.h"
#include "Symbol.h"
#include "SymbolTable.h"
#include "Tokenizer.h"
#include "Ark.h"

Naview::Naview(void): m_pxOut(&std::cout)
{
	m_pxCurrentScope = NULL;
	m_eScopeType = Scope::eScopeInvalid;
	m_sFilename = "";
	m_sOutputFile = "";
	m_nLines = 0;
	m_nLevel = 0;
	m_bScopeResolvingNeeded = false;
	m_bIncludeFileResolving = false;
}

Naview::~Naview(void)
{
	if(m_sOutputFile!="")
		delete m_pxOut;
}

void Naview::setOutputFile(std::string sOutFile)
{
	sOutFile = sOutFile + ".uboat.html";
	Neptuner_createDirectoryFor(sOutFile);
	m_pxOut =  new std::ofstream(sOutFile.c_str());
	m_sOutputFile = sOutFile;
}


void Naview::offScopeEntry(Scope& inNewScope, Scope& inOldScope)
{
	m_pxCurrentScope = &inNewScope;
	m_eScopeType = m_pxCurrentScope->getScopeType();
	createSpanForScope();
}

void Naview::offScopeExit(Scope& inNewScope, Scope& inOldScope)
{
	createSpanEnd();
	m_pxCurrentScope = &inNewScope;
	m_eScopeType = m_pxCurrentScope->getScopeType();
}

void Naview::getSafeHTML(Token& inToken, std::string& sTokenHTML){
	for(size_t i=0; i<inToken.m_sValue.length(); i++){
		char cTest = inToken.m_sValue[i];

		switch (cTest) {
			case '&':
				sTokenHTML += "&amp;";
				break;
			case '<':
				sTokenHTML += "&lt;";
				break;
			case '>':
				sTokenHTML += "&gt;";
				break;
			case '"':
				sTokenHTML += "&quot;";
				break;
			case '\t':
				sTokenHTML += "&nbsp;&nbsp;&nbsp;&nbsp;"; 
				//TODO: Replace with string padding function
				break;
			case ' ':
				sTokenHTML += "&nbsp;";
				break;
			case '\r':
				sTokenHTML = ""; //Otherwise \r\n sequences will produce two <BR/>s 
				//TODO: Replace with lookahead strategy
				break;
			case '\n':
				sTokenHTML ="<BR/>";
				break;
			default:
				sTokenHTML += cTest;
				break;
		}
	}
}

void Naview::makeFileLink(std::string& sFilename, std::string& sFullFilename, std::string& sTokenHTML)
{
	//<a href="#bookmark">HTML a tag example</a> 
	sTokenHTML="";

	std::string sTargetFile = Ark_getCodebaseLocation(sFullFilename);
	if(sTargetFile != ""){
		sTokenHTML += "<a href='";
		sTargetFile += ".uboat.html";
		std::string sRelativizer = Neptuner_getRelativizer(m_nLevel)+"codebase/";
		sTargetFile = sRelativizer + sTargetFile;
		sTokenHTML += sTargetFile;
		sTokenHTML += "'>";
		sTokenHTML += sFilename;
		sTokenHTML += "</a>";
	}
	else{
		sTokenHTML = sFilename;
	}
}

void Naview::checkIncludeFile(Token &inToken, std::string& sTokenHTML)
{
	static char cStarter = '\0';
	static char cEnder = '\0';
	static std::string sFilename = "";
	bool   bEndMode = false;

	if(!m_bIncludeFileResolving)
	{
		if(inToken.m_sValue != "include") return;
		m_bIncludeFileResolving = true;
		sFilename = "";
		cStarter = '\0';
		cEnder = '\0';
		return;
	}

	char cDelimiter = char(inToken);
	//In file inclusion mode
	if(cStarter)
		sTokenHTML = "";
	
	switch(cDelimiter){
		case '"':
			if(cStarter == cDelimiter){
				std::string sFullFilename = Ark_getAbsolutePath(sFilename);
				
				makeFileLink(sFilename, sFullFilename, sTokenHTML);
				bEndMode = true;
			}
			else{
				cStarter = cDelimiter;
			}
			break;

		case ' ':
			if(cStarter)
				break;
			break;

		case '\r':
		case '\n':
			bEndMode = true;

		default:
			sFilename += inToken.m_sValue;
			break;
	}
	
	if(bEndMode){
		m_bIncludeFileResolving = false; //End of mode
		if(cStarter == '"')
                    sTokenHTML+="&quot;";
		cStarter = '\0';
		cEnder = '\0';
		sFilename ="";
	}
}

void Naview::offNewToken(Scope& inScope, Token& inToken)
{
	std::string sTokenHTML="";
	
	getSafeHTML(inToken, sTokenHTML);

	switch( m_eScopeType)
	{
	case Scope::eScopeCommentC:
	case Scope::eScopeCommentCPP:
	case Scope::eScopeLiteral:
		break;

	case Scope::eScopePreprocessor:
		checkIncludeFile(inToken, sTokenHTML);
		break;

	default:
		if(Keywords::isKeyword(sTokenHTML))	{
			sTokenHTML = ("<span class=kwrd>" + sTokenHTML + "</span>");
		}
		else{
			if(inToken.m_bIsDelimiter){
				if( (inToken.m_sValue == ".") || (inToken.m_sValue == "->") )
					m_bScopeResolvingNeeded = true;
				else{
					if(inToken.m_sValue!=" ")	{
						m_bScopeResolvingNeeded = false;
						m_xLastSymbol = Symbol();
					}
				}
				break;
			}

			Symbol xSymbol;
			bool bSymbolFound = false;
			if(!m_bScopeResolvingNeeded){
				bSymbolFound = inScope.findSymbol(inToken.m_sValue, xSymbol);
			}
			else{
				Scope* pxScope=NULL;
				switch(m_xLastSymbol.m_eSymbolType){
					case Symbol::eSymbolVariable:
						inScope.findScope(m_xLastSymbol.m_sTypePrefix, pxScope);
						if(pxScope)
							bSymbolFound = pxScope->findSymbol(inToken.m_sValue, xSymbol);
						break;

					default:
						break;
				}
			}

			if(bSymbolFound){
				getSymbolLink(xSymbol, sTokenHTML);
				m_xLastSymbol = xSymbol;
			}//if
		}//else
		break;
	}//switch

    *m_pxOut<<sTokenHTML;

	char cDelimiter = char(inToken);
	if('\n' == cDelimiter)		//Intentionally not coupling with switch case, this is not tr functionality
		createNewline();
}

void Naview::getSymbolLink(Symbol& xSymbol, std::string& sTokenHTML){
	//<a href="#bookmark">HTML a tag example</a> 
	sTokenHTML="";
	sTokenHTML += "<a href='";
	if(m_pxLocation->m_sFilename != xSymbol.m_xDefinition.m_sFilename)
	{
		std::string sTargetFile = Ark_getCodebaseLocation(xSymbol.m_xDefinition.m_sFilename);
		sTargetFile += ".uboat.html";
		std::string sRelativizer = Neptuner_getRelativizer(m_nLevel)+"codebase/";
		sTargetFile = sRelativizer + sTargetFile;

		sTokenHTML += sTargetFile;
	}
	
	sTokenHTML += "#SL";
	sTokenHTML += stringify(xSymbol.m_xDefinition.m_nLineNumber);
	sTokenHTML += "'>";
	sTokenHTML += xSymbol.m_sName;
	sTokenHTML += "</a>";

}

void Naview::createNewline()
{
	createSpanEnd();

	std::string sLineHTML="";
	sLineHTML += "</span></li>\n";
	sLineHTML += "<li><span class='sl' id= 'SL";
	sLineHTML += stringify(m_pxLocation->m_nLineNumber);
	sLineHTML += "'>";
	*m_pxOut<<sLineHTML<<endl;

	createSpanForScope();
}

void Naview::createSpanForScope()
{
	switch(m_eScopeType)
	{
	case Scope::eScopeCommentC:
	case Scope::eScopeCommentCPP:
		*m_pxOut << "<span class=comm>";
		break;

	case Scope::eScopePreprocessor:
		*m_pxOut << "<span class=prep>";
		break;

	case Scope::eScopeLiteral:
		*m_pxOut << "<span class=litr>";
		break;
	}
}

void Naview::createSpanEnd()
{
	switch(m_eScopeType)
	{
	case Scope::eScopeCommentC:
	case Scope::eScopeCommentCPP:
	case Scope::eScopePreprocessor:
	case Scope::eScopeLiteral:
		*m_pxOut<< "</span>";
		break;
	}
}

void Naview::generateHeader(){
std::string sNeptunerHome = Neptuner_getRelativizer(m_nLevel);
std::string sRelativizer = sNeptunerHome +"uboat/";

*m_pxOut<<"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"<<endl;
*m_pxOut<<"<html>"<<endl;
*m_pxOut<<"<head>"<<endl;
*m_pxOut<<"<title>"<< m_sFilename << "</title>"<<endl;
*m_pxOut <<"<link rel='shortcut icon' type='image/ico' href='"<<sRelativizer<<"neptunerIcon.jpg'>"<<endl;

*m_pxOut<<"<link rel='stylesheet' href='" << sRelativizer << "neptuner.css' type='text/css' media='screen'>"<<endl;
*m_pxOut<<"<link rel='stylesheet' href='" << sRelativizer << "uboat.css' type='text/css' media='screen'>"<<endl;
*m_pxOut<<"<link rel='stylesheet' href='" << sRelativizer << "menu.css' type='text/css' media='screen'>"<<endl;

*m_pxOut<<"<script type='text/javascript' src='" << sRelativizer << "prototype.js'> </script>"<<endl;
*m_pxOut<<"<script type='text/javascript' src='" << sRelativizer << "shortcuts.js'> </script>"<<endl;
*m_pxOut<<"<script type='text/javascript' src='" << sRelativizer << "editinplace.js'> </script>"<<endl;
*m_pxOut<<"<script type='text/javascript' src='" << sRelativizer << "inplacenote.js'> </script>"<<endl;
*m_pxOut<<"<script type='text/javascript' src='" << sRelativizer << "protofish.js'> </script>"<<endl;
*m_pxOut<<"<script type='text/javascript' src='" << sRelativizer << "proto.menu.js'> </script>"<<endl;
*m_pxOut<<"<script type='text/javascript' src='" << sRelativizer << "saver.js'> </script>"<<endl;
*m_pxOut<< "<script type='text/javascript'>var nSourceLines = "<< m_nLines<<";";
*m_pxOut<<"var sUBoatHome='"<<sRelativizer<<"';";
*m_pxOut<<"var sNeptunerHome='"<<sNeptunerHome<<"';";
*m_pxOut<<"var sFilename='"<<m_sFilename<<"';";
*m_pxOut<<"</script>"<<endl;
*m_pxOut<<"</head>"<<endl;
}

void Naview::generateBody()
{
std::string sRelativizer = Neptuner_getRelativizer(m_nLevel)+"uboat/";
*m_pxOut<<"<body>"<<endl;
*m_pxOut<<"<script type='text/javascript' src='" << sRelativizer << "model.js'></script>"<<endl;
*m_pxOut<<"<script type='text/javascript' src='" << sRelativizer << "view.js'></script>"<<endl;
*m_pxOut<<"<script type='text/javascript' src='" << sRelativizer << "uboat.js'></script>"<<endl;
*m_pxOut<<"<script type='text/javascript' src='" << sRelativizer << "controller.js'></script>"<<endl;
*m_pxOut<<"<div id=paneUBoat>"<<endl;
*m_pxOut<<"<div id=barTitle></div>"<<endl;
*m_pxOut<<"<div id=barMenu></div>"<<endl;
*m_pxOut<<"<div id=areaBookmarks><span id=barBookmarks></span><ul id=listBookmarks></ul></div>"<<endl;
*m_pxOut<<"<div id=dispScrapbook><span id=barScrapbook></span><ul id=listScraps></ul></div>"<<endl;
*m_pxOut<<"<div id=barStatus></div>"<<endl;
*m_pxOut<<"</div>"<<endl;	
*m_pxOut<<"<div id=paneSidebar>"<<endl;
*m_pxOut<<"</div>"<<endl;
*m_pxOut<<"<div id=paneSource>"<<endl;
*m_pxOut<<"<ol>"<<endl;
*m_pxOut<<"<li><span class='sl' id= 'SL1'>"<<endl;	
//The rest to be filled in dynamically, during the walk
}


void Naview::generateFooter()
{
*m_pxOut<< "</span></li>" << endl;
*m_pxOut<< "</ol>" << endl;
*m_pxOut<<"</div>"<<endl;
*m_pxOut<< "</body>" << endl << "</html>" << endl;

}